package org.wang.generalcode.support.config;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.mock.MockDriver;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import jakarta.annotation.Resource;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.wang.generalcode.support.config.properties.DruidProperties;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * druid 配置多数据源
 *
 * @author wangchong
 */
@Configuration
public class DruidConfig {

	@Bean(name = "master")
	@ConfigurationProperties("spring.datasource.druid.master")
	public DataSource masterDataSource(DruidProperties druidProperties) {
		final DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
		return druidProperties.dataSource(dataSource);
	}

	@Bean(name = "slave")
	@ConfigurationProperties("spring.datasource.druid.slave")
	@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true", matchIfMissing = false)
	public DataSource slaveDataSource(DruidProperties druidProperties) {
		final DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
		return druidProperties.dataSource(dataSource);
	}


	@EventListener(ContextRefreshedEvent.class)
	public void context(ContextRefreshedEvent event) {
		final ApplicationContext ac = event.getApplicationContext();
		final String[] beanNamesForDataSource = ac.getBeanNamesForType(DataSource.class);
		final String[] finalBeanNames = ArrayUtil.removeEle(beanNamesForDataSource, "dynamicDataSource");
		for (final String beanName : finalBeanNames) {
			final DataSource dataSource = ac.getBean(beanName, DataSource.class);
			connectPropertiesForOracle((DruidDataSource) dataSource);
		}
	}

	/**
	 * <p> Oracle时候 设置数据报告中的jdbc thin client module名称
	 * <p> {@link https://www.cnblogs.com/xibuhaohao/p/11765433.html}
	 * @param datasource
	 */
	private void connectPropertiesForOracle(DruidDataSource datasource) {
		if (!isOracle(datasource)) {
			return;
		}

		final java.util.Properties props = new java.util.Properties();
		props.put("v$session.program", System.getProperties().get("spring.application.name"));
		datasource.setConnectProperties(props);
	}

	private boolean isOracle(DruidDataSource datasource) {
		if (datasource.isOracle()) {
			return true;
		}

		if(datasource.getRawDriver() == MockDriver.instance){
			return false;
		}

		final String databaseProductName = ReflectUtil.<String> invoke(new VendorDatabaseIdProvider(),
				"getDatabaseProductName", datasource);
		return "Oracle".equalsIgnoreCase(databaseProductName);
	}

	// ======================================================================================== 二选一
	/**
	 * <p>
	 * druid 配置多数据源
	 * <p>
	 * 借助第三方库, 基于注解 @DS在Mapper方法上实现数据源切换, 有以下问题: 事务下无法实现数据源切换 ( 参见：
	 * https://github.com/baomidou/dynamic-datasource-spring-boot-starter/wiki/Transation-Questions)
	 * <p>
	 * 和 {@code KanqDruidQdDynamicDataSourceConfig } 互斥
	 */
	@Configuration
	@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = false)
	public static class DruidCommonDynamicDataSourceConfig {

		@Resource
		private ApplicationContext ac;

		@Resource
		private DynamicDataSourceProperties properties;

		@Bean
		@Primary
		public DynamicDataSourceProvider dynamicDataSourceProvider(
				@Qualifier("master") DataSource masterDataSource) {

			// 第一个参数为默认的DataSource
			// 第二个参数为映射关系
			return new DynamicDataSourceProvider() {

				@Override
				public Map<String, DataSource> loadDataSources() {
					final Map<String, DataSource> targetDataSources = new HashMap<>();
					final String[] beanNamesForDataSource = ac.getBeanNamesForType(DataSource.class);
					final String[] finalBeanNames = ArrayUtil.removeEle(beanNamesForDataSource, "dynamicDataSource");
					for (final String beanName : finalBeanNames) {
                        targetDataSources.put(StrUtil.subBefore(beanName, "GenDataSource", true),
								ac.getBean(beanName, DataSource.class));
					}

					// 默認master
					targetDataSources.put("master", masterDataSource);
					return targetDataSources;
				}
			};
		}

		@Bean(name = "dynamicDataSource")
		@Primary
		public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
			DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
			dataSource.setPrimary(properties.getPrimary());
			dataSource.setStrict(properties.getStrict());
			dataSource.setStrategy(properties.getStrategy());
//			dataSource.setProvider(dynamicDataSourceProvider);
			dataSource.setP6spy(properties.getP6spy());
			dataSource.setSeata(properties.getSeata());
			return dataSource;
		}
	}

}
